Skip to content

Octatrack base: module machines play right after a boot, the FLEX switch reloads live parameters, SRC label after a boot; USB bridge opens the Octatrack - #1024

Merged
repeat98 merged 40 commits into
repeat98:mainfrom
npp1993:claude/project-thread-iw03rg
Oct 30, 2026

Conversation

@npp1993

@npp1993 npp1993 commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Three Octatrack base fixes for module machines (Analog BD, FM Synth and the others that run on a FLEX track), combined on one branch as agreed, plus the USB bridge fix they were tested with. Author: Nathaniel Pavel (npp1993).

  • What was wrong or hard:
    1. After a power cycle, PLAY on MODULES LOADED left Analog BD silent until the next stop: an on-demand engine was placed only while the sequencer was stopped and settled, a controller RESET went one 24-word packet a 60 Hz tick (about 2.6 s a core for Analog BD), and the restore said MODULES LOADED before any engine ran.
    2. FLEX ↔ module machine switch: the track's live parameters stayed on the old machine's values until SOURCE CLEAR, so a machine chosen from FLEX could stay silent and FLEX chosen back played the machine's settings.
    3. After a boot, a restored module-machine track showed "SRC > FLEX" until it was tapped.
    4. USB bridge: with another Elektron device on USB (an Analog Rytm on Overbridge) the bridge opened the wrong device and every request to the Octatrack failed.
  • What this changes:
    1. An engine is placed while playing when it fits in free words (dl_engine_claim commit 1); trims, evacuations and unplacing still wait for a stopped, settled unit, and nothing is placed while a pattern change is queued. RESET jobs carry up to eight packets. MODULES LOADED waits for modwerk_controller_settling() (0.5 s at least, 10 s at most; a closed core, a lost core state, a key bus that cannot come up or a stuck engine ends the wait). A packaged feature counts as used once a track of the bank's Parts is signed for its module's machine (modwerk_machine_bank_modules), not only from a track's first trig. bind_live keeps a module's retry when a later module in the same pass binds. Owner decision asked: this relaxes the "no placement while playing" rule in docs/OCTATRACK_RESIDENT_ENGINES_ON_DEMAND.md (the doc is updated to say what still waits for a stop).
    2. A FLEX ↔ module-machine switch reloads the track's live parameters from its Part, as stock does when a switch changes the machine type (machine_registry.c). STATIC and the other stock machines are unchanged.
    3. runtime.c redraws the track header after the modules restore (the stock header drawer at 0x4004d780).
    4. scripts/octatrack-usb-bridge.py opens the device that offers the Modwerk interface.
      Changelog: changelog/2026-10-27-engines-play-after-boot.md, 2026-10-27-flex-machine-switch-live.md, 2026-10-28-boot-src-label.md, 2026-10-27-usb-bridge-elektron-device.md. The beta base is regenerated (base-binding.json, the engine assets, the base-bound packages).
  • How I checked it: npm run pr:ready (on this Mac only test_published_module_packages_rebuild_byte_for_byte fails, on clean main too: the Tape Echo standalone recipe; CI has the inputs). Host: test_resident_engines_board.py modes 1, 13, 46, 54, 64; test_resident_transport.c, test_modset.c, test_machine_registry.c, test_runtime.c, sdk/tests/test_usb_bridge.py. On the MKII (OS 1.40C), recorded over USB, see docs/verification/2026-10-27-engines-play-after-boot.md: on a development twin, a FLEX → Analog BD switch while playing was placed on both cores within a second and bound in 1.8 s; a bank change into an Analog BD bank while playing placed it in about 1 s with T2 and T3 bit-exact across the change; on a flashed personal base (14 modules), MODULES LOADED then PLAY sounded Analog BD on the first step, and T1 showed SRC → ANALOG BD without a tap on every boot; FLEX/STATIC → Analog BD played without SOURCE CLEAR in a blank project. On the final build (ccc53692 = this branch plus Analog BD 0.1.6, Nathaniel's own project, 28 October) all three pass again: SRC label, first trig after MODULES LOADED, FLEX ↔ Analog BD switch. Not fixed here, and older than this change: a DSP halt in the first minute after a cold boot (DSP STOPPED, residency off for the boot). It happened with FUNC held (no modules restored), so it is in the base; the five stall records are decoded in the verification record and point at a lost frame-interrupt edge, not at these changes. Open too: Ringmod SC refused at the boot restore on some bases (RUNTIME_BUSY), a first-trig stutter after Analog BD → FLEX, and an FM Synth switch untested.
  • What the guides lacked or got wrong, and where I fixed it: docs/OCTATRACK_RESIDENT_ENGINES_ON_DEMAND.md (placement while playing; an edited elekloader file the resident patch touches needs its pin and patch context updated), docs/OCTATRACK_DRIVING.md (the bridge and a second Elektron device), docs/OCTATRACK_BETA_TEST_PLAN.md, docs/module-guides/machines.md (the switch reload), the two verification records.

Please merge #944 (Analog BD 0.1.6) first if you can; this one then regenerates its base on top. Either order works, whichever lands second regenerates the base.

🤖 Generated with Claude Code

npp1993 and others added 23 commits October 27, 2026 16:17
…ck's live parameters

A module machine is FLEX underneath, so a switch between FLEX and a
module machine keeps the stored type, and stock only loads a track's
live parameters from its Part (0x40001f18) where a commit changes the
type. The live copy stayed on the old machine's values until SOURCE
CLEAR. mw_assign now calls stock's own routine when it changes the
track's machine but not its stored type. Host test covers both
directions, the same machine again, FLEX to FLEX and STATIC <-> machine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nd relink the packages

octatrack:base --beta --write and octatrack:module-packages (only
requiredBase moved). The Tape Echo and Euclid standalone recipes re-pin
module-build.ts and the card-build assets repeat98#993 changed, or
octatrack:module-packages refuses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…vice; guide traps

With an Analog Rytm on Overbridge also on USB the bridge opened the first
Elektron device and every request failed. It now matches the device that
offers the Modwerk vendor interface (test_usb_bridge.py). The driving
guide records the trap; the machines guide adds the FLEX switch check and
that a machine with a DSP engine is silent after boot until the unit has
sat stopped. .gitignore's node_modules entry also matches a linked
node_modules (a worktree's symlink), which node_modules/ did not.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PLAY pressed on MODULES LOADED left Analog BD silent until the transport
had stopped for a few seconds (flashed 5afa623e, 27 October 2026). Three
changes, host-tested; the base is not regenerated yet:

- An engine that fits in free words is placed, reset and enabled while
  the sequencer plays (dl_engine_claim commit 1). Trims, evacuations and
  unplacing stay stopped-and-settled; nothing starts while a pattern
  change is queued. This relaxes the stopped-only placement rule.
- A controller RESET job carries up to eight packets (DL_RESET_DATA,
  192 words), so Analog BD's 3,776-word reset is 20 board ticks, not 158.
- MODULES LOADED waits until the trusted controllers the bank uses are
  ACTIVE (modwerk_controller_settling): at least 0.5 s, at most 10 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…boot

Rebuilt with npm run octatrack:base -- --beta --write from 899eabc's sources:
base identity 9d5af41e (was 903f8ed6). The lease entries moved, so Analog
BD's reviewed recipe was recaptured (imageSha256 only) and the base-bound
packages relinked (--base-only): Sidechain Compressor 0.2.0, Output Matrix
0.1.1 and Analog BD 0.1.5 get new bytes and trust rows; CC Map and Recorder
Loop Fix keep theirs and only name the new base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
The fix grew two documents already over 32 KB, which npm run check refuses.
The 15 October integration-build notes and "Where this stands" move to
docs/verification/2026-10-15-resident-engines-integration-build.md (the
beta test plan's pointer follows). The after-boot trap leaves
OCTATRACK_HARDWARE_TRAPS.md: it describes only the base before this fix,
and the engines guide's 27 October update and diagnostics carry it. The
Beta base note now relinks with --base-only, which works where the full
run stops at a standalone recipe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
- The MODULES LOADED wait counts a slot only while the bank uses its feature,
  and ends a slot's wait when its owned channel closed or its key bus cannot
  come up this boot, not only on the board's own transport closing.
- bind_live keeps an earlier module's retry when a later module in the same
  pass binds, so the notice cannot come before that module is bound.
- SOME MODULES FAILED and MODULES NOT LOADED are said at once; a held
  MODULES LOADED is dropped when the host changes the set.
- A play-time claim told its code must move waits for the stop instead of
  planning again on every board tick.
- The guide states the play-time rule as current; the history and the unit
  results go to docs/verification/2026-10-27-engines-play-after-boot.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…Parts carry its machine

After a power cycle Analog BD's renderer asks for the write lease only from a
track's first trig, so its engine started loading at PLAY and MODULES LOADED
had nothing to wait for (personal base on the owner's MKII: the first 6 trigs
missed). A packaged feature now counts as used once a track of the bank's
Parts, working or saved, is signed for its module's machine
(machine_registry.c modwerk_machine_bank_modules, read by use_tick), so the
engine loads during the boot and the notice waits for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…after a boot

The saved set comes back about three seconds after the first draw. The
base's redraw after a change of live modules (runtime.c, 0x4004d948(-1))
redraws the track screen but not its header, stock's 0x4004d780, which a
track select and SRC SETUP's commit call. So a module machine's track,
FLEX underneath, kept "SRC > FLEX" until tapped (Nathaniel's MKII,
28 October 2026, Analog BD on T1 after a cold boot of d4658748). The
redraw now draws the header too. Host test: every redraw draws it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
npm run octatrack:base -- --beta --write: identity 3a883bd1. The lease
entries did not move, so Analog BD's recipe and the controller trust rows
are unchanged; the base-bound packages only name the new base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
… use

npm run octatrack:base -- --beta --write from d7117e7's sources: identity
a48ff191. The lease entries moved, so Analog BD's recipe was recaptured
(imageSha256 only) and the base-bound packages relinked with --base-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…r a boot

The watchdog shut the loader off whenever the frames stood still for half a
second with any loader job pending. Right after a power-on stock holds the
frames itself (a boot's project load, the first bank change after it): on the
owner's MKII both stalls had no transfer phase active, the eDMA DONE and only a
job's answer pending. Half a second now applies only while one of our
transfers is mid-frame; a pending answer waits ten seconds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…tchdog change

3747310 changed dsp.c, so the resident build refused it (the pinned source
hash, then the patch context at the watchdog line). The pin now names the new
dsp.c, and resident_base.patch's dsp.c hunk is rebased onto it: the hunk only
moves, its one context line is the new watchdog call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
npm run octatrack:base -- --beta --write: identity 0295f2b8. The lease
entries did not move: the recipe and trust rows are unchanged and the
base-bound packages only name the new base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…pin and patch context

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
… at half a second

On the owner's MKII the 10 s wait (3747310) let the frame chain die for good:
a halt that recover() used to bring back after half a second (1db64a78, three
boots) froze the sequencer until a power cycle (aed618ef, two boots, one with
no modules restored). The halts are real, not stock pauses. This reverts
3747310, b9a3169 and a069405 together, back to 568d4e3's dsp.c, pin, patch
and beta base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
claude/ot-boot-src-label (90f6008) carries claude/ot-base-flex-live-refresh
(595771d): the mw_assign live-parameter reload, the USB bridge picking the
Octatrack, their guide traps, and the SRC label redraw after a boot. The beta
base files conflict and keep this branch's for now; they are regenerated next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
test_machine_registry.c keeps both sides' memory (the saved Parts this branch
reads, main's clamps). Generated base files, recipes and packages take main's
and are regenerated next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqJqc4odc7Z37VTLM4FtYG
…early-boot DSP halt, which this change does not fix

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…itch and SRC label fixes on main d2cb12d

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ober

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
npp1993 and others added 2 commits October 28, 2026 13:05
…-iw03rg

# Conflicts:
#	docs/module-guides/machines.md
#	sdk/machines/octatrack/base-binding.json
#	sdk/machines/octatrack/elekloader/modset.c
#	src/engine/assets/octatrack-base-fills.json
#	src/engine/assets/octatrack-base-sources.json
#	src/engine/assets/octatrack-base-variants.json
#	src/engine/assets/octatrack-base.elemod
#	src/engine/assets/octatrack-module-packages.json
… card frame guard)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@repeat98

Copy link
Copy Markdown
Owner

Thanks, this is a big step for module machines after a boot, and the verification record makes it easy to follow. A few things before it can go in:

  1. The last merge of main dropped a line from Fix: Octatrack card builds, too many module voices no longer stall the unit (frame guard) #1030. In docs/module-guides/machines.md, "A bounded render" now says "card builds have no guard" again. Main says card builds' core logger does the same (sdk/runtime/logging/frame_guard.c) and logs the dropouts as GRD. Please take main's sentence back; your new "Switching sounds right at once" item above it is fine.
  2. The bank scan runs on every tick, with interrupts masked. use_tick() calls modwerk_machine_bank_modules() from inside modwerk_controller_tick()'s modwerk_machine_lock() section. It scans 8 Parts × 8 tracks for each machine, for as long as a held packaged feature isn't seen yet. When Analog BD is installed but this bank doesn't use it, "not seen yet" stays true, so the scan runs every tick for the whole session. Please scan only when the bank or a Part changes (or a machine is assigned), or every N ticks, or outside the lock. Or measure it with the guard (npm run device -- guard) in a busy pattern with Analog BD installed but unused, and put the number in the record.
  3. The SRC label changelog (changelog/2026-10-28-boot-src-label.md) says "not yet on a unit". Your record has it passing on every boot on the flashed base and on ccc53692, so please say that.
  4. POLY8 and VECTOR. They have their own assign, so mw_assign returns before relive() when you switch FLEX → POLY8/VECTOR. Switching back to FLEX now reloads the live parameters. That's probably right, but it changes how those two leave a track, and nobody has listened to it yet. Either check POLY8 → FLEX and VECTOR → FLEX on the unit, or say in the machine-switch changelog which machines it covers ("Analog BD, FM Synth and the others" reads wider than it is, and FM Synth hasn't been tried yet).
  5. Small: the .gitignore change (node_modules/ → node_modules) isn't part of this fix. Please drop it, or tell me why it's needed.

On placing engines while playing: that's fine with me, as long as point 2 keeps the bank scan off every tick. Once #944 or #1033 lands first, this needs main merged in and the base regenerated (and #1033 also adds a test mode 64, so whichever goes second renumbers).

…n a change, docs, changelogs)

The bank's Parts are read again only when the bank, the Part or a machine assignment changed, or every 64th tick, not every tick inside the lock. machines.md keeps main's card-guard sentence; the SRC label changelog says the unit result; the switch changelog names Analog BD as heard and FM Synth, POLY8 and VECTOR as untried; the .gitignore change is dropped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
npp1993 and others added 9 commits October 29, 2026 04:05
…t-thread-iw03rg

# Conflicts:
#	public/octatrack-modules/analog-bassdrum/0.1.6-experimental.mwrm
#	sdk/machines/octatrack/base-binding.json
#	sdk/machines/octatrack/module-adapters/controller-policies.json
#	sdk/machines/octatrack/module-adapters/machine-recipes.json
#	src/engine/assets/octatrack-base-sources.json
#	src/engine/assets/octatrack-base-variants.json
#	src/engine/assets/octatrack-base.elemod
#	src/engine/assets/octatrack-module-packages.json
… (recipe recaptured)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…omes held (the boot restore); the board test signs its track as an assignment

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…recipe recaptured)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…-iw03rg

# Conflicts:
#	sdk/machines/octatrack/base-binding.json
#	sdk/tests/test_resident_engines_board.py
#	src/engine/assets/octatrack-base-sources.json
#	src/engine/assets/octatrack-base-variants.json
#	src/engine/assets/octatrack-base.elemod
#	src/engine/assets/octatrack-module-packages.json
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…W resolves no weak variable)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@repeat98

Copy link
Copy Markdown
Owner

Thanks, that covers all five points. The bank scan now runs only when something changes, or every 64th tick, and the docs and changelogs read right.

One new thing: module-paths-windows fails in the board test with undefined reference to modwerk_machine_assigned. MinGW doesn't resolve the weak, uninitialised volatile uint32_t modwerk_machine_assigned in controller_board.c, so the fixture's extern has nothing to link to. It's the only weak variable in the tree; the weak functions are fine. On PE/COFF a weak tentative definition stays a common symbol, so it never gets defined. Giving it an initialiser (__attribute__((weak)) volatile uint32_t modwerk_machine_assigned = 0;) should be enough. Or define it in the board fixture, as the fixture already does for modwerk_controller_lease_seen, if controller_board.c is always linked beside machine_registry.c. Linux passes, so this only affects the Windows link.

Your flashed runs came before the scan change. On the base you test #1032 with, could you also do one cold boot with Analog BD on T1 and press PLAY as soon as MODULES LOADED shows? For what it's worth, I merged #1032 into this branch locally: the sources merge cleanly and the machine-registry and board host tests pass together. Only the generated base files need regenerating.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@repeat98

Copy link
Copy Markdown
Owner

Thanks, Windows is green now. One question about how it's fixed: controller_board.c now defines volatile uint32_t modwerk_machine_assigned; itself, and machine_registry.c:303 defines it too. Both go into the base image, compiled with -fno-common. So either the base's link merges them, or it keeps two copies. If it keeps two, use_tick() reads a counter that mw_assign never increments, and on a unit the rescan on assignment never fires; only the every-64-ticks path does. The board test can't show which, because the fixture increments the counter itself.

Could you check in the built base that there's one modwerk_machine_assigned, the registry's (for example from the link map, or a symbol listing of the ColdFire objects)? If there are two, make the one in controller_board.c an extern, since it always links beside the registry in the base, and give the board fixture its own definition.

npp1993 and others added 4 commits October 29, 2026 09:12
…-iw03rg

# Conflicts:
#	sdk/machines/octatrack/base-binding.json
#	src/engine/assets/octatrack-base-fills.json
#	src/engine/assets/octatrack-base-sources.json
#	src/engine/assets/octatrack-base-variants.json
#	src/engine/assets/octatrack-base.elemod
#	src/engine/assets/octatrack-module-packages.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t-thread-iw03rg

# Conflicts:
#	sdk/machines/octatrack/base-binding.json
#	sdk/machines/octatrack/elekloader/test_machine_registry.c
#	src/engine/assets/octatrack-base-sources.json
#	src/engine/assets/octatrack-base-variants.json
#	src/engine/assets/octatrack-base.elemod
#	src/engine/assets/octatrack-module-packages.json
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@repeat98
repeat98 merged commit f78480b into repeat98:main Oct 30, 2026
8 checks passed
repeat98 added a commit to stigmagit/modwerk that referenced this pull request Oct 30, 2026
Takes main's USB bridge (repeat98#1024 carries the same fix) and drops this
branch's duplicate; generated base files regenerate next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@npp1993
npp1993 deleted the claude/project-thread-iw03rg branch October 30, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants